// singleflight.go
// 实现简单的SingleFlight
// 用以解决缓存击穿问题

package singleflight

import (
	"fmt"
	"sync"
)

// call结构体用于阻塞多次调用，调用一次函数，返回调用的结果
type call struct {
	wg  sync.WaitGroup // 阻塞多次调用
	val interface{}    // 函数返回的结果
	err error          // 函数返回的错误
}

// group结构体用于记录key对应的call
type Group struct {
	mu sync.Mutex       // 保护map
	m  map[string]*call // 每一个key对应的call结构体
}

func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
	// 根据key判断当前函数是不是在调用中
	// 涉及到对map的新建和修改，加锁保护
	fmt.Println("get flag 1")
	g.mu.Lock()
	if g.m == nil {
		g.m = make(map[string]*call)
	}

	// key存在，表明当前函数正在被执行
	if c, ok := g.m[key]; ok {
		g.mu.Unlock()
		c.wg.Wait()
		fmt.Println("flag 3: val: ", c.val, "err: ", c.err)
		return c.val, c.err
	}

	// key不存在，将key插入m中，执行函数
	c := new(call)
	c.wg.Add(1)
	g.m[key] = c
	g.mu.Unlock()

	c.val, c.err = fn()
	c.wg.Done()

	// 执行完毕，删除key
	g.mu.Lock()
	delete(g.m, key)
	g.mu.Unlock()

	fmt.Println("[in SingleFlight]: val: ", c.val, "err: ", c.err)
	return c.val, c.err
}
